package Tests
{
	import Box2D.Collision.Shapes.b2CircleShape;
	import Box2D.Collision.Shapes.b2EdgeShape;
	import Box2D.Collision.Shapes.b2PolygonShape;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Common.Math.b2Vec2Array;
	import Box2D.Common.b2Color;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2BodyDef;
	import Box2D.Dynamics.b2BodyType;
	import Box2D.Dynamics.b2FixtureDef;
	
	import Framework.Test;
	
	public class EdgeShapes extends Test
	{
		private static const e_maxBodies:int = 256;
		
		private var m_bodyIndex:int;
		private var m_bodies:Vector.<b2Body> = new Vector.<b2Body>(e_maxBodies);
		private var m_polygons:Vector.<b2PolygonShape> = new Vector.<b2PolygonShape>(4);
		private var m_circle:b2CircleShape = new b2CircleShape;
		
		private var m_angle:Number;
		
		public function EdgeShapes()
		{
			super();	
			for(var i:int=0;i<m_polygons.length;i++)
			{
				m_polygons[i] = new b2PolygonShape;
			}
			createGround();
			preCreateShapes()
			m_bodyIndex = 0;			
			m_angle = 0.0;
		}
		private function createGround():void{
			var x1:Number = -20;
			var y1:Number = 2 * Math.cos(x1/10*Math.PI);
			for(var i:int=0;i<80;++i){
				var x2:Number = x1+.5;
				var y2:Number = 2*Math.cos(x2/10*Math.PI);
				var shape:b2EdgeShape = new b2EdgeShape;
				shape.Set(new b2Vec2(x1,y1),new b2Vec2(x2,y2));
				m_groundBody.CreateFixtureByShape(shape,0);
				x1 = x2;
				y1 = y2;
			}
		}
		private function preCreateShapes():void{
			var vertices:b2Vec2Array = new b2Vec2Array(8);
			vertices.setAt2(0,-0.5, 0.0);
			vertices.setAt2(1,0.5, 0.0);
			vertices.setAt2(2,0.0, 1.5);
			m_polygons[0].Set(vertices,3);
			
			vertices.setAt2(0,-0.1, 0.0);
			vertices.setAt2(1,0.1, 0.0);
			vertices.setAt2(2,0.0, 1.5);
			m_polygons[1].Set(vertices, 3);
			
			var w:Number = 1.0;
			var b:Number = w / (2.0 + Math.sqrt(2.0));
			var s:Number = Math.sqrt(2.0) * b;
			vertices.setAt2(0,0.5 * s, 0.0);
			vertices.setAt2(1,0.5 * w, b);
			vertices.setAt2(2,0.5 * w, b + s);
			vertices.setAt2(3,0.5 * s, w);
			vertices.setAt2(4,-0.5 * s, w);
			vertices.setAt2(5,-0.5 * w, b + s);
			vertices.setAt2(6,-0.5 * w, b);
			vertices.setAt2(7,-0.5 * s, 0.0);
			
			m_polygons[2].Set(vertices, 8);

			m_polygons[3].SetAsBox(0.5, 0.5);

			m_circle.radius = 0.5;
		}
		private function Create(index:int):void
		{
			if (m_bodies[m_bodyIndex] != null)
			{
				m_world.DestroyBody(m_bodies[m_bodyIndex]);
				m_bodies[m_bodyIndex] = null
			}
			
			var bd:b2BodyDef = new b2BodyDef;
			
			var x:Number = RandomFloat(-10.0, 10.0);
			var y:Number = RandomFloat(10.0, 20.0);
			bd.position.Set(x, y);
			bd.angle = RandomFloat(-Math.PI, Math.PI);
			bd.type = b2BodyType.b2_dynamicBody;
			
			if (index == 4)
			{
				bd.angularDamping = 0.02;
			}
			
			m_bodies[m_bodyIndex] = m_world.CreateBody(bd);
			
			var fd:b2FixtureDef = new b2FixtureDef;
			if (index < 4)
			{
				fd.shape = m_polygons[index];
				fd.friction = 0.3;
				fd.density = 20.0;
				m_bodies[m_bodyIndex].CreateFixture(fd);
			}
			else
			{
				fd.shape = m_circle;
				fd.friction = 0.3;
				fd.density = 20.0;
				m_bodies[m_bodyIndex].CreateFixture(fd);
			}
			
			m_bodyIndex = (m_bodyIndex + 1) % e_maxBodies;
		}
		
		private function DestroyBody():void
		{
			for (var i:int = 0; i < e_maxBodies; ++i)
			{
				if (m_bodies[i] != null)
				{
					m_world.DestroyBody(m_bodies[i]);
					m_bodies[i] = null;
					return;
				}
			}
		}
		public override function Key(key:String):void
		{
			switch (key)
			{
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
					Create(int(key) - 1);
					break;
				
				case 'd':
					DestroyBody();
					break;
			}
		}
		public override function get hotKeyNote():String{
			return "Press 1-5 to drop stuff, [d] to destroy body";
		}
		public override function Step():void
		{
			var advanceRay:Boolean = !paused || steping;
			
			super.Step();			
			
			var L:Number = 25.0;
			var point1:b2Vec2 = new b2Vec2(0.0, 10.0);
			var d:b2Vec2 = new b2Vec2(L * Math.cos(m_angle), -L * Math.abs(Math.sin(m_angle)));
			var point2:b2Vec2 = point1.Add(d)
			
			var callback:EdgeShapesCallback = new EdgeShapesCallback;
			
			m_world.RayCast(callback, point1, point2);
			
			if (callback.m_fixture)
			{
				m_debugDraw.DrawPoint(callback.m_point, 5.0, b2Color.from(0.4, 0.9, 0.4));
				
				m_debugDraw.DrawSegment(point1, callback.m_point, b2Color.from(0.8, 0.8, 0.8));
				
				var head:b2Vec2 = callback.m_point.Add(callback.m_normal.Multiply(.5));
				m_debugDraw.DrawSegment(callback.m_point, head, b2Color.from(0.9, 0.9, 0.4));
			}
			else
			{
				m_debugDraw.DrawSegment(point1, point2, b2Color.from(0.8, 0.8, 0.8));
			}
			
			if (advanceRay)
			{
				m_angle += 0.25 * Math.PI / 180.0;
			}
		}
		
	}
}
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2Fixture;
import Box2D.Dynamics.b2RayCastCallback;

class EdgeShapesCallback extends b2RayCastCallback
{
	public function EdgeShapesCallback()
	{
	}
	public override function ReportFixture(fixture:b2Fixture, point:b2Vec2, normal:b2Vec2, fraction:Number):Number
	{
		m_fixture = fixture;
		m_point = point;
		m_normal = normal;
		
		return fraction;
	}
	
	public var m_fixture:b2Fixture;
	public var m_point:b2Vec2;
	public var m_normal:b2Vec2;
};
